#include<stdio.h>
/*
																memcpy函数解析

函数概要：

	memcpy 函数从 src 指向的内存空间拷贝 n 个字节到 dest 指向的内存空间。src 和 dest 指向的内存区域不能出现重叠，否则应该使用 memmove 函数。
	memcpy 函数并不关心被复制的数据类型，只是逐字节地进行复制，这给函数的使用带来了很大的灵活性，可以面向任何数据类型进行复制。注意这样子同时会出现错误
	内存重叠

	可以看到memcpy()函数功能主要是：

	从‘源头指向的内存块’ ‘拷贝固定字节数的数据’到 ‘目标指向的内存块’.

	与strcpy()函数不同的是,该函数不会检查任何终止字符(如'\0'),而总是精确的拷贝参数传入的字节数.

函数原型：
	
	#include <string.h>
	...
	void *memcpy(void *dest, const void *src, size_t n);

参数解析：
	
	void *dest -------- 指向用于存储复制内容的目标数组，类型强制转化为void* 指针
	const void *str   -------- 指向要复制的数据源<不可被修改，常量>，类型强制转化为void* 指针
	size_t n  -------- 要被复制的字符数大小<size_t(无类型的整型)>


返回值：
	
	函数的返回值类型是无类型指针(void*),它的作用是在函数运行结束后返回拷贝后的目的地内存块的起始地址.

本次函数参考文献：
	memcpy()函数详解(内存块拷贝函数) https://blog.csdn.net/weixin_72357342/article/details/132104889?sharetype=blog&shareId=132104889&sharerefer=APP&sharesource=2403_87271668&sharefrom=qq
	memcpy()函数原型 https://www.runoob.com/cprogramming/c-function-memcpy.html
	内存重叠 https://zhuanlan.zhihu.com/p/616652634

*/

#include<stdio.h>
#include<stdlib.h>
#include<string.h>


//使用memcpy拷贝字符数组数据

#if 0
int main(void)
{
	char str[] = "Hello,world";
	char* ptr;
	int length = sizeof(str);


	ptr = (char*)malloc(length * sizeof(char));
	if (ptr == NULL)
	{
		printf("内存分配失败");
		exit(1);
	}

	memset(ptr, 0, length);//将ptr动态数组中的数据全部初始化为0

	memcpy(ptr, str, 5);
	printf("%s", ptr);

	
	return 0;
}

#endif


//使用memcpy拷贝结构体数据
#if 0


#include<stdio.h>
#include<string.h>

struct STRUCT
{
	char name[20];
	int age;

};

int main()
{
	struct STRUCT person = { "Pierre de Fermat",46 };
	struct STRUCT person_copy = { 0 };

	/*使用memcpy拷贝结构体*/
	memcpy(&person_copy, &person, sizeof(person));
	printf("person_copy:%s %d\n", person.name, person.age);
	return 0;

}


#endif

//模拟实现memcpy函数功能
#if 0 

#include<assert.h>
//断言函数

#include<stdio.h>

void* my_memcpy(void* destination, const void* source, size_t num)
{
	//判断是否为空指针
	assert(destination);//断言语句，条件为假，则执行下面的代码，否则返回错误
	assert(source);

	void* rec = destination;
	while (num--)
	{
		*(char*)destination = *(char*)source;
		destination = (char*)destination + 1;

		source = (char*)source + 1;
	}

	return rec;


}

int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[10] = { 0 };
	my_memcpy(arr2, arr1, 20);
	for (int i = 0; i < sizeof(arr2) / sizeof(arr2[0]); i++)
	{
		printf("%d\n", arr2[i]);
	}

	return 0;
}


#endif

//内存重叠1
//内存重叠是指在内存中存在两个或多个区域，它们的地址范围有交叉部分
//有关内存重叠的知识这篇文章均有提到 https://zhuanlan.zhihu.com/p/616652634 请参考这篇文章

//memcpy内存重叠
#if 0

#include<stdio.h>


int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };

	memcpy(arr + 2, arr, 5*sizeof(arr[0]));
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}


#endif

//内存重叠2
//使用my_memcpy模拟出内存重叠

#if 1

#include<stdio.h>
#include<assert.h>

void* my_memcpy(void* destination, const void* source, size_t n)
{
	if (destination == NULL) exit(1);
	if (source == NULL) exit(1);
	
	void* rec = destination;


	while (n--)
	{
		*(char*)destination = *(char*)source;
		destination = (char*)destination + 1;
		source = (char*)source + 1;
	
	}
	
	return rec;

}



int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memcpy(arr + 2, arr, 20);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

//为了避免这样的情况使用memmove更加安全一些
//在前面的演示中使用vs2022编译器测试memcpy()函数拷贝重叠内存块仅仅是因为vs编译器对memcpy库函数的实现较为严谨,
// 当我们换用别的编译器时,这个结果就有可能发生变化,但无论各家编译器的底层实现逻辑是怎样的,我们最好都以C标准的定义为准来使用各个库函数


#endif